<!doctype html>
<html lang="cn">
<head>
	<meta charset="UTF-8">
	<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
	<meta http-equiv="X-UA-Compatible" content="ie=edge">
	<title>圆球错觉</title>
	<style>
		*{ margin: 0; padding: 0; box-sizing: border-box; }
		ul, ol{ list-style: none; }
		#root{
			display: flex; align-items: center; justify-content: center;
			width: 100vw; height: 100vh;
			overflow: hidden;
		}
		.panel-wrap{ position: fixed; top: 0; left: 0; min-width: 100vw; }
		.panel-wrap .panel{ background-color: rgba(50, 50, 50, .1);}
		.panel-wrap .btn-number{width: 42px;}
		.panel-wrap .panel-info{
			width: fit-content;
			background-color: rgba(50, 50, 50, .1);
			font-size: 14px;
		}
		.panel-wrap .panel-info li{ display: flex; align-items: center; padding: 3px 0; border-top: 1px dashed #666666; }
		.panel-wrap .panel-info li:first-child{border-top: 1px dashed transparent;}
		.panel-wrap .panel-info .info-title{ width: 70px; margin-right: 4px; text-align: right; font-weight: bold; }
		.panel-wrap .panel-info .info-content{ min-width: 80px; text-align: left; }
		.panel-wrap .panel-info .info-content .flex{display: flex; flex-wrap: wrap;}
		.panel-wrap .panel-info .info-content .show-value{ flex: auto; width: 42px; text-align: center; }
		.panel-wrap .panel-info .info-content input[type="range"]{ width: 150px; }
		.center{}
		.center .circle{
			position: relative;
			display: flex;
			justify-content: center;
			align-items: center;
			width: 300px; height: 300px;
			border-radius: 300px;
			border: 1px solid red;
		}
		.center .circle ul{ position: relative; width: 100%; }
		.center .circle ul li{
			position: absolute; top: 0; left: 0;
			transform-origin: center;
			width: 100%;
		}
		.center .circle ul li .dot{
			position: absolute; top: 0; left: 0; transform: translate(-50%, -50%);
			width: 15px; height: 15px; background-color: #111111;border-radius: 15px;
			animation: 3s slide infinite ease-in-out;
		}
		@keyframes slide{
			0%{ margin-left: 0; }
			50%{ margin-left: 100%; }
			100%{ margin-left: 0; }
		}
	
	</style>

</head>
<body>
<!--
                  Sorry, nothing to see here.

                       8888  8888888
                  888888888888888888888888
               8888:::8888888888888888888888888
             8888::::::8888888888888888888888888888
            88::::::::888:::8888888888888888888888888
          88888888::::8:::::::::::88888888888888888888
        888 8::888888::::::::::::::::::88888888888   888
           88::::88888888::::m::::::::::88888888888    8
         888888888888888888:M:::::::::::8888888888888
        88888888888888888888::::::::::::M88888888888888
        8888888888888888888888:::::::::M8888888888888888
         8888888888888888888888:::::::M888888888888888888
        8888888888888888::88888::::::M88888888888888888888
      88888888888888888:::88888:::::M888888888888888   8888
     88888888888888888:::88888::::M::;o*M*o;888888888    88
    88888888888888888:::8888:::::M:::::::::::88888888    8
   88888888888888888::::88::::::M:;:::::::::::888888888
  8888888888888888888:::8::::::M::aAa::::::::M8888888888       8
  88   8888888888::88::::8::::M:::::::::::::888888888888888 8888
 88  88888888888:::8:::::::::M::::::::::;::88:88888888888888888
 8  8888888888888:::::::::::M::"@@@@@@@"::::8w8888888888888888
  88888888888:888::::::::::M:::::"@a@":::::M8i888888888888888
 8888888888::::88:::::::::M88:::::::::::::M88z88888888888888888
8888888888:::::8:::::::::M88888:::::::::MM888!888888888888888888
888888888:::::8:::::::::M8888888MAmmmAMVMM888*88888888   88888888
888888 M:::::::::::::::M888888888:::::::MM88888888888888   8888888
8888   M::::::::::::::M88888888888::::::MM888888888888888    88888
 888   M:::::::::::::M8888888888888M:::::mM888888888888888    8888
  888  M::::::::::::M8888:888888888888::::m::Mm88888 888888   8888
   88  M::::::::::::8888:88888888888888888::::::Mm8   88888   888
   88  M::::::::::8888M::88888::888888888888:::::::Mm88888    88
   8   MM::::::::8888M:::8888:::::888888888888::::::::Mm8     4
       8M:::::::8888M:::::888:::::::88:::8888888::::::::Mm    2
      88MM:::::8888M:::::::88::::::::8:::::888888:::M:::::M
     8888M:::::888MM::::::::8:::::::::::M::::8888::::M::::M
    88888M:::::88:M::::::::::8:::::::::::M:::8888::::::M::M
   88 888MM:::888:M:::::::::::::::::::::::M:8888:::::::::M:
   8 88888M:::88::M:::::::::::::::::::::::MM:88::::::::::::M
     88888M:::88::M::::::::::*88*::::::::::M:88::::::::::::::M
    888888M:::88::M:::::::::88@@88:::::::::M::88::::::::::::::M
    888888MM::88::MM::::::::88@@88:::::::::M:::8::::::::::::::*8
    88888  M:::8::MM:::::::::*88*::::::::::M:::::::::::::::::88@@
    8888   MM::::::MM:::::::::::::::::::::MM:::::::::::::::::88@@
     888    M:::::::MM:::::::::::::::::::MM::M::::::::::::::::*8
     888    MM:::::::MMM::::::::::::::::MM:::MM:::::::::::::::M
      88     M::::::::MMMM:::::::::::MMMM:::::MM::::::::::::MM
      88    MM:::::::::MMMMMMMMMMMMMMM::::::::MMM::::::::MMM
        88    MM::::::::::::MMMMMMM::::::::::::::MMMMMMMMMM
         88   8MM::::::::::::::::::::::::::::::::::MMMMMM
          8   88MM::::::::::::::::::::::M:::M::::::::MM
              888MM::::::::::::::::::MM::::::MM::::::MM
             88888MM:::::::::::::::MMM:::::::mM:::::MM
             888888MM:::::::::::::MMM:::::::::MMM:::M
            88888888MM:::::::::::MMM:::::::::::MM:::M
           88 8888888M:::::::::MMM::::::::::::::M:::M
           8  888888 M:::::::MM:::::::::::::::::M:::M:
              888888 M::::::M:::::::::::::::::::M:::MM
             888888  M:::::M::::::::::::::::::::::::M:M
             888888  M:::::M:::::::::@::::::::::::::M::M
             88888   M::::::::::::::@@:::::::::::::::M::M
            88888   M::::::::::::::@@@::::::::::::::::M::M
           88888   M:::::::::::::::@@::::::::::::::::::M::M
          88888   M:::::m::::::::::@::::::::::Mm:::::::M:::M
          8888   M:::::M:::::::::::::::::::::::MM:::::::M:::M
         8888   M:::::M:::::::::::::::::::::::MMM::::::::M:::M
        888    M:::::M:::::::::::::::::::::::MMM:::::::::M::::M
      8888    MM::::Mm:::::::::::::::::::::MMMM:::::::::m::m:::M
    888      M:::::M::::::::::::::::::::MMM::::::::::::M::mm:::M
  8888       MM:::::::::::::::::::::::::MM:::::::::::::mM::MM:::M:
             M:::::::::::::::::::::::::M:::::::::::::::mM::MM:::Mm
            MM::::::m:::::::::::::::::::::::::::::::::::M::MM:::MM
            M::::::::M:::::::::::::::::::::::::::::::::::M::M:::MM
           MM:::::::::M:::::::::::::M:::::::::::::::::::::M:M:::MM
           M:::::::::::M88:::::::::M:::::::::::::::::::::::MM::MMM
           M::::::::::::8888888888M::::::::::::::::::::::::MM::MM
           M:::::::::::::88888888M:::::::::::::::::::::::::M::MM
           M::::::::::::::888888M:::::::::::::::::::::::::M::MM
           M:::::::::::::::88888M:::::::::::::::::::::::::M:MM
           M:::::::::::::::::88M::::::::::::::::::::::::::MMM
           M:::::::::::::::::::M::::::::::::::::::::::::::MMM
           MM:::::::::::::::::M::::::::::::::::::::::::::MMM
            M:::::::::::::::::M::::::::::::::::::::::::::MMM
            MM:::::::::::::::M::::::::::::::::::::::::::MMM
             M:::::::::::::::M:::::::::::::::::::::::::MMM
             MM:::::::::::::M:::::::::::::::::::::::::MMM
              M:::::::::::::M::::::::::::::::::::::::MMM
              MM:::::::::::M::::::::::::::::::::::::MMM
               M:::::::::::M:::::::::::::::::::::::MMM
               MM:::::::::M:::::::::::::::::::::::MMM
                M:::::::::M::::::::::::::::::::::MMM
                MM:::::::M::::::::::::::::::::::MMM
                 MM::::::M:::::::::::::::::::::MMM
                 MM:::::M:::::::::::::::::::::MMM
                  MM::::M::::::::::::::::::::MMM
                  MM:::M::::::::::::::::::::MMM
                   MM::M:::::::::::::::::::MMM
                   MM:M:::::::::::::::::::MMM
                   MMM::::::::::::::::::MMM
                    MM::::::::::::::::::MMM
                     M:::::::::::::::::MMM
                    MM::::::::::::::::MMM
                    MM:::::::::::::::MMM
                    MM::::M:::::::::MMM:
                    mMM::::MM:::::::MMMM
                     MMM:::::::::::MMM:M
                     mMM:::M:::::::M:M:M
                      MM::MMMM:::::::M:M
                      MM::MMM::::::::M:M
                      mMM::MM::::::::M:M
                       MM::MM:::::::::M:M
                       MM::MM::::::::::M:m
                       MM:::M:::::::::::MM
                       MMM:::::::::::::::M:
                       MMM:::::::::::::::M:
                       MMM::::::::::::::::M
                       MMM::::::::::::::::M
                       MMM::::::::::::::::Mm
                        MM::::::::::::::::MM
                        MMM:::::::::::::::MM
                        MMM:::::::::::::::MM
                        MMM:::::::::::::::MM
                        MMM:::::::::::::::MM
                         MM::::::::::::::MMM
                         MMM:::::::::::::MM
                         MMM:::::::::::::MM
                         MMM::::::::::::MM
                          MM::::::::::::MM
                          MM::::::::::::MM
                          MM:::::::::::MM
                          MMM::::::::::MM
                          MMM::::::::::MM
                           MM:::::::::MM
                           MMM::::::::MM
                           MMM::::::::MM
                            MM::::::::MM
                            MMM::::::MM
                            MMM::::::MM
                             MM::::::MM
                             MM::::::MM
                              MM:::::MM
                              MM:::::MM:
                              MM:::::M:M
                              MM:::::M:M
                              :M::::::M:
                             M:M:::::::M
                            M:::M::::::M
                           M::::M::::::M
                          M:::::M:::::::M
                         M::::::MM:::::::M
                         M:::::::M::::::::M
                         M;:;::::M:::::::::M
                         M:m:;:::M::::::::::M
                         MM:m:m::M::::::::;:M
                          MM:m::MM:::::::;:;M
                           MM::MMM::::::;:m:M
                            MMMM MM::::m:m:MM
                                  MM::::m:MM
                                   MM::::MM
                                    MM::MM
                                     MMMM

-->
<div id="root">
	<div class="panel-wrap">
		<div class="panel">
			<h2>圆球错觉</h2>
		</div>
		<ul class="panel-info">
			<li v-for="item of info">
				<div class="info-title">{{dict[item]}} :</div>
				<div class="info-content">
					<div class="flex">
						<span class="show-value">{{$data[item].min}}</span>
						<label>
							<input type="range" name=""
								   v-model="$data[item].value"
								   :max="$data[item].max"
								   :min="$data[item].min"
								   :step="$data[item].step">
						</label>
						<span class="show-value">{{$data[item].max}}</span>
					</div>
					<div class="flex">
						<button class="btn-number" @click="$data[item].value -= $data[item].step">-</button>
						<span class="show-value">{{$data[item].value}}</span>
						<button class="btn-number" @click="add($data[item])">+</button>
					</div>
				</div>
			</li>
		</ul>
	</div>
	<div class="center">
		<div class="circle">
			<ul>
				<template v-if="display">
					<li v-for="li of (count.value * 1)"
						:class="'ms_' + animationDelay(li) "
						:style="{transform: `rotateZ(${(li - 1) * (360 / count.value)}deg)`}">
						<div class="dot"
							 :style="{'animation-delay':    animationDelay(li) + 'ms','animation-duration': duration.value + 'ms'}"></div>
					</li>
				</template>
			</ul>
		</div>
	</div>
</div>
<script src="./lib/vue.js"></script>
<script>
  new Vue({
    el: '#root',
    data: {
      count: {
        value: 9,  // 圆球个数,双向绑定之后是字符串,需要转成数字类型
        max: 48,
        min: 1,
        step: 1,
      },
      duration: {
        value: 5000,  // 动画时间(ms),双向绑定之后是字符串,需要转成数字类型
        max: 20000,
        min: 0,  // 0 时停止动画
        step: 500,
      },
      info: ['count', 'duration'],
      display: true,  // 每次数量变化的时候 需要重新渲染所有列表,否则动画会不整齐
      dict: {
        count: '黑点数量',
        duration: '动画时间',
      },
    },
    computed: {
      animationDelay () {
        return function (index) {
          // 列表下标 默认是从1开始,这里计算成从0开始
          return this.duration.value / this.count.value * (index - 1)
        }
      },
    },
    methods: {
      // 验证被修改的值
      validateRange (rule, value) {
        let amend = value <= rule.min ? rule.min : value >= rule.max ? rule.max : value
        console.warn(`超出范围: 当前值: ${value} ,已修正为: ${amend}`)
        return amend
      },
      // 加法计算,如果遇到字符串 需要处理成数字类型; 减法计算,需要处理精度问题,改用最小单位整数
      add (item) {
        item.value = item.value * 1 + item.step
      }
    },
    watch: {
      count: {
        // 深度监听数据的变化
        deep: true,
        handler (newValue, oldValue) {
          console.log(newValue.value, oldValue.value)
          // 如果修改后的值不符合要求；将数量限制在规定范围内
          if (!(this.count.min <= newValue.value && newValue.value <= this.count.max)) {
            this.count.value = this.validateRange(this.count, oldValue.value)
            return
          }
          this.display = false
          this.$nextTick(function () {
            this.display = true
          })
        },
      },
      duration: {
        // 深度监听数据的变化
        deep: true,
        handler (newValue, oldValue) {
          if (!(this.duration.min <= newValue.value && newValue.value <= this.duration.max)) {
            this.duration.value = this.validateRange(this.duration, oldValue.value)
          }
        },
      },
    },
    filters: {
      msTos (value) {
        return value / 1000
      }
    }
  })
</script>
</body>
</html>
